#!/usr/bin/env python

from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import ServerFactory

from fgs.proxy import error

class FgsLibProtocol(LineReceiver):

    delimiter = "\n\0\n"

    def __init__(self):
        import logging
        self._log = logging.getLogger('fgslib.protocol')

    def connectionMade(self):
        self._log.info("Client connected to proxy")
        self._gameserver = None

    def lineReceived(self, line):
        data = line.split('\n')
        if len(data) == 0:
            self._log.info("Empty message received, severing connection.")
            self.transport.loseConnection()
        msg = data[0].lower()
        args = data[1:]

        f = getattr(self, "msg_" + msg, None)
        if not f:
            self._log.info("Unknown message %s received, severing connection." % msg)
            self.transport.loseConnection()
        else:
            try:
                self._log.debug("Process %s", msg)
                result = f(*args)
                if result != None:
                    if isinstance(result, (list, tuple)):
                        self._send_success(*result)
                    else:
                        self._send_success(result)
            except error.FgsProxyError, e:
                self._send_error(e)

    def connectionLost(self, plop):
        if hasattr(self, '_gameserver'):
            self._unregister_server()

    def _send_reply(self, *msg):
        reply = '\n'.join(msg)
        self.sendLine(reply)

    def _send_success(self, *args):
        self._send_reply(*(['OK']+list(args)))

    def _send_error(self, e):
        from twisted.python.failure import Failure
        if isinstance(e, Failure):
            e = e.value
        self._send_reply(*(['ERROR', '0', str(e)]))

    def _is_registered(self):
        return self._gameserver != None

    def _check_is_registered(self):
        if not self._is_registered():
            raise error.FgsProxyError("Game server not registered")

    def _unregister_server(self):
        self.factory.proxy.del_gameserver(self._gameserver)
        del self._gameserver

    def msg_registerserver(self, game_name, game_version, listen_port):
        def registerserver_success(gameserver):
            self._log.debug("Registration successful, notifying game server")
            self._send_success()
            self._gameserver = gameserver
        def registerserver_error(e):
            self._log.error("%s", e.value)
            self._log.error("Registration failed, notifying game server")
            self._send_error("Failed to register.")

        if self._is_registered():
            raise error.FgsProxyError("Game server already registered")

        self._log.info("Registering %s v%s on port %s" % (game_name,
                                                   game_version,
                                                   listen_port))
        d = self.factory.proxy.new_gameserver(game_name,
                                              game_version,
                                              listen_port)
        d.addCallbacks(registerserver_success, registerserver_error)

    def msg_getconfig(self):
        def config_generator(config):
            for config_item in config:
                yield config_item

        self._check_is_registered()

        if not hasattr(self, '_config'):
            self._config = config_generator(self._gameserver.get_config())

        try:
            return self._config.next()
        except StopIteration:
            del self._config
            return []

    def msg_authplayerjoin(self, login, token, nonce):
        self._check_is_registered()

        def authplayerjoin_success(player):
            self._log.debug("Authenticated player successfully joined, "
                            "notifying game server")
            self._send_success(player.uuid)
        def authplayerjoin_error(e):
            self._log.debug("Authenticated player join failed: %s", e.value)
            self._send_error(e)

        d = self._gameserver.add_player(login, token, nonce)
        d.addCallbacks(authplayerjoin_success, authplayerjoin_error)

    def msg_anonplayerjoin(self):
        self._check_is_registered()

        def success(player):
            self._log.debug("Anonymous player successfully joined, "
                            "notifying game server")
            self._send_success(player.uuid)
        def error(e):
            self._log.debug("Anonymous player join failed: %s", e.value)
            self._send_error(e)

        self._gameserver.add_player().addCallbacks(success, error)

    def msg_playerupdate(self, player_uuid, key, val):
        self._check_is_registered()
        player = self._gameserver.players[player_uuid]
        player[key] = val
        self._log.debug("%s[%s] = %s", player.uuid, key, val)
        return ()

    def msg_commitplayer(self, player_uuid=None):
        self._check_is_registered()
        if player_uuid != None:
            player = self._gameserver.players[player_uuid]
            player.commit()
        else:
            for player in self._gameserver.players.values():
                player.commit()
        return ()

    def msg_newmatch(self):
        self._check_is_registered()
        match = self._gameserver.add_match()
        return match.uuid

    def msg_matchupdate(self, match_uuid, key, val):
        self._check_is_registered()
        match = self._gameserver.matches[match_uuid]
        match[key] = val
        return ()

    def msg_commitmatch(self, match_uuid=None):
        self._check_is_registered()

        if match_uuid != None:
            match = self._gameserver.matches[match_uuid]
            match.commit()
        else:
            for match in self._gameserver.matches.values():
                match.commit()
        return ()

    def msg_joinmatch(self, match_uuid, player_uuid):
        self._check_is_registered()

        match = self._gameserver.matches[match_uuid]
        player = self._gameserver.players[player_uuid]
        match.add_player(player)
        return ()

    def msg_matchplayerupdate(self, match_uuid, player_uuid, key, value):
        self._check_is_registered()

        match = self._gameserver.matches[match_uuid]
        player = self._gameserver.players[player_uuid]

        match.players[player.uuid][key] = value
        return ()

    def msg_leavematch(self, match_uuid, player_uuid):
        self._check_is_registered()

        match = self._gameserver.matches[match_uuid]
        player = self._gameserver.players[player_uuid]

        match.del_player(player)
        return ()

    def msg_delmatch(self, match_uuid):
        self._check_is_registered()

        match = self._gameserver.matches[match_uuid]
        self._gameserver.del_match(match.uuid)
        return ()

    def msg_playerleave(self, player_uuid):
        self._check_is_registered()

        player = self._gameserver.players[player_uuid]
        self._gameserver.del_player(player.uuid)
        return ()

    def msg_unregisterserver(self):
        self._check_is_registered()

        self.factory.proxy.del_gameserver(self._gameserver)
        del self._gameserver
        return ()

class FgsLibFactory(ServerFactory):
    protocol = FgsLibProtocol

    def __init__(self, proxy):
        self.proxy = proxy
